#!/usr/bin/env python
#
'''
Created on 2009-4-6

@author: Wang_Yuan
'''

import os
import re
import wx

from PrintPanel_xrc import *

class PrintPanel(xrcPrintPanel):
    
    def __init__(self, parent, vocabulary):
        xrcPrintPanel.__init__(self, parent)
        self.vocabulary = vocabulary
        self.__initData()
        self.__initCtrl()
        self.__initEvents()
        
    def __initData(self):
        # record the checked status of every words
        self.dataDict = {}
        self.allUnchecked = {}
        self.allChecked = {}
        self.checkedCountInList = 0
        # all the data shown in list
        self.dataList = []
        for word in self.vocabulary.words:
            self.allUnchecked[word] = False
            self.allChecked[word] = True
        self.dataDict = self.allChecked.copy()
        self.necessaryList = self.vocabulary.getExamWordList()
        
    def __initCtrl(self):
        self.search = wx.SearchCtrl(self.searchPanel, style=wx.TE_PROCESS_ENTER)
        self.search.ShowSearchButton(True)
        self.search.ShowCancelButton(True)
        sizer = self.searchPanel.GetSizer()
        sizer.Add(self.search, proportion=1, flag=wx.EXPAND)
        sizer.Fit(self.searchPanel)
        
        self.refreshCheckState()
        self.refreshWordsList()
        self.Refresh()
        
    def __initEvents(self):
        self.search.Bind(wx.EVT_TEXT_ENTER, self.OnSearchWord)
        self.search.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN, self.OnSearchWord)
        self.search.Bind(wx.EVT_SEARCHCTRL_CANCEL_BTN, self.OnCancelSearch)
    
    def getDataList(self):
        if self.necessaryCheck.IsChecked():
            return self.necessaryList
        else:
            return self.vocabulary.data
    
    def __filterFunc(self, data):
        pattern = self.search.GetValue().lstrip()
        if pattern:
            return bool(re.search(pattern, data.word, re.UNICODE))
        else:
            return True
    
    def refreshWordsList(self):
        self.dataList = filter(self.__filterFunc, self.getDataList())
        self.wordsList.Set(map(lambda x: x.word, self.dataList))
        self.checkedCountInList = 0
        for i in xrange(self.wordsList.GetCount()):
            checked = bool(self.dataDict.get(self.wordsList.GetString(i)))
            self.wordsList.Check(i, checked)
            if checked:
                self.checkedCountInList += 1
        print self.checkedCountInList
        self.refreshCheckState()
        
    def refreshCheckState(self):
        canPrint = True
        if self.checkedCountInList <= 0:
            self.selectAll.SetValue(False)
            canPrint = False
        elif self.checkedCountInList >= self.wordsList.GetCount():
            self.selectAll.SetValue(True)
        else:
            self.selectAll.Set3StateValue(wx.CHK_UNDETERMINED)
            
        self.previewButton.Enable(canPrint)
        self.checkedCountLabel.SetLabel(unicode(self.checkedCountInList))
        self.Refresh()
            
    def OnSearchWord(self, evt):
        self.refreshWordsList()
    
    def OnCancelSearch(self, evt):
        self.search.ChangeValue('')
        self.refreshWordsList()
    
    def OnCheckbox_necessaryCheck(self, evt):
        self.refreshWordsList()
        
    def OnChecklistbox_wordsList(self, evt):
        index = evt.GetInt()
        word = self.wordsList.GetString(index)
        checked = self.wordsList.IsChecked(index)
        self.dataDict[word] = checked
        self.checkedCountInList += 1 if checked else -1
        self.refreshCheckState()
        
    def OnCheckbox_selectAll(self, evt):
        print evt.IsChecked()
        self.dataDict = self.allChecked.copy() if evt.IsChecked() else self.allUnchecked.copy()
        self.refreshWordsList()
    
    def OnButton_pageSetupButton(self, evt):
        printer = getPrinter()
        printer.pageSetup(self)

    def OnButton_previewButton(self, evt):
        if self.dataList:
            printList = []
            for i in xrange(self.wordsList.GetCount()):
                if self.wordsList.IsChecked(i):
                    printList.append(self.dataList[i])
            if printList:
                printer = getPrinter()
                printer.printPreview(self, printList)
            else:
                wx.MessageBox('No word is selected to print!')

class PrintDialog(wx.Dialog):
    def __init__(
            self, parent, vocabulary, ID=wx.ID_ANY, title='Print Vocabulary', 
            style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER,
            ):
        '''
        Constructor
        '''
        
        wx.Dialog.__init__(self, parent, ID, title, style=style)
        sizer = wx.BoxSizer(wx.VERTICAL)
        panel = PrintPanel(self, vocabulary)
        sizer.Add(panel, proportion=1, flag=wx.ALL|wx.EXPAND, border=10)
        
        self.SetSizer(sizer)
        sizer.Fit(self)
    
class VocabularyPrinter:
    __instance = None
    
    @classmethod
    def getInstance(cls):
        if not cls.__instance:
            cls.__instance = cls()
        return cls.__instance
    
    def __init__(self):
        self.psdd = wx.PageSetupDialogData()
        printData = self.psdd.GetPrintData()
        printData.SetPaperId(wx.PAPER_A4)
        printData.SetPrintMode(wx.PRINT_MODE_PRINTER)
        printData.SetOrientation(wx.LANDSCAPE)
        self.psdd.EnableMargins(False)
        self.psdd.CalculatePaperSizeFromId()
        
    def pageSetup(self, parent):
        dialog = wx.PageSetupDialog(parent, self.psdd)
        dialog.ShowModal()
        self.psdd = wx.PageSetupDialogData(dialog.GetPageSetupData())
        self.psdd.EnableMargins(False)
        dialog.Destroy()
        
    def printPreview(self, parent, dataList):
        data = wx.PrintDialogData(self.psdd.GetPrintData())
        previewPrintout = VocabularyPrintout(dataList, self.psdd)
        printPrintout = VocabularyPrintout(dataList, self.psdd)
        
        preview = wx.PrintPreview(previewPrintout, printPrintout, data)
        preview.SetZoom(50)
        
        if not preview.IsOk():
            wx.MessageBox('Something wrong with your printer setup!', 
                          caption='warning', 
                          style=wx.OK|wx.ICON_WARNING)
            return
        
        while not isinstance(parent, wx.Frame):
            parent = parent.GetParent()
        pfrm = wx.PreviewFrame(preview, parent, 'Preview and print selected words')
        pfrm.Initialize()
        pfrm.SetSize((700,550))
        pfrm.Show(True)

class VocabularyPrintout(wx.Printout):
    def __init__(self, dataList, psdd):
        wx.Printout.__init__(self)
        self.dataList = dataList
        self.psdd = psdd
        self.sepLimits = (4700, 4700)
        self.margin = (100, 0)
        self.border = (10, 10)
        
        self.fonts = [wx.Font(56, wx.SWISS, wx.NORMAL, wx.NORMAL), 
                      wx.Font(40, wx.SWISS, wx.NORMAL, wx.NORMAL), 
                      wx.Font(56, wx.SWISS, wx.NORMAL, wx.NORMAL), 
                      wx.Font(32, wx.SWISS, wx.NORMAL, wx.NORMAL)]
        self.font = self.fonts[3]
        self.fontHeightFactor = 1.5
        self.blockPen = wx.BLACK_PEN
        self.wordPen = wx.BLACK_DASHED_PEN
        
    def OnPreparePrinting(self):
        'prepare printing'
        self.pageSize = self.GetPageSizePixels()
        print self.pageSize
        self.seps = map(lambda x: x[0]/x[1]+1, zip(self.pageSize, self.sepLimits))
        blockSize = map(lambda x: x[0] / x[1], zip(self.pageSize, self.seps))
        print blockSize
        
        self.dc = self.GetDC()
        dcSize = self.dc.GetSizeTuple()
        print 'dc size:', dcSize
        self.scale = min(float(dcSize[0]) / self.pageSize[0], float(dcSize[1]) / self.pageSize[1])
        print 'scale:', self.scale
        self.dc.SetUserScale(self.scale, self.scale)
        self.dc.SetFont(self.font)
        self.fontHeightFactor = float(self.dc.GetTextExtent('ABCDEFGabcdefg0123456789')[1]) / self.font.GetPointSize()
        
        self.pages = []
        index = 0
        while index < len(self.dataList):
            blocks = []
            for n in xrange(self.seps[1]):
                for m in xrange(self.seps[0]):
                    x = blockSize[0] * m
                    y = blockSize[1] * n
                    print (x,y)
                    self.dc.SetDeviceOrigin(x * self.scale, y * self.scale)
                    block, index = self.prepareBlock(blockSize, index)
                    if block:
                        blocks.append(block)
            if blocks:
                self.pages.append(blocks)
                
        text = ''
        for page in self.pages:
            for block in page:
                for word in block:
                    for data in word[0]:
                        text += os.linesep.join(data)
                text += "-----------\n"
                print 'block'
            text += '=============\n'
            print 'page'
#        wx.CallAfter(wx.MessageBox, text)
        print 'Prepare Finished'
        
    def prepareBlock(self, size, index):
        block = []
        writableRect = [self.margin[0],               #left
                        self.margin[1],               #top
                        size[0] - self.margin[0],     #right
                        size[1] - self.margin[1]]     #bottom
        height = 0
        while index < len(self.dataList):
            wordData = self.dataList[index]
            wordPrintInfo, height = self.prepareWord(wordData, writableRect)
            y = writableRect[1]
            writableRect[1] += (height + (self.border[1]<<1))
            if writableRect[1] < writableRect[3]:
                print 'prepare y:', y
                block.append((wordPrintInfo, y))
                index += 1
            else:
                break
        
        return block, index
    
    def prepareWord(self, wordData, writableRect):
        unit = ((writableRect[2] - writableRect[0]) >> 2)
        exampleTextList = list()
        for e in wordData.examples:
            exampleTextList.append(e.originalPart())
            if e.translation:
                exampleTextList.append(e.translation)
        texts = [# word
                 [wordData.word], 
                 # meanings
                 wordData.getNonEnglishMeaningList() + wordData.getEnglishMeaningList(), 
#                 [' || '.join(wordData.getNonEnglishMeaningList()), 
#                  ' || '.join(wordData.getEnglishMeaningList())], 
                 # pronunciation
                 [wordData.pronunciation], 
                 # examples
                 exampleTextList
                 ]
        wordPrintInfo = [None, None, None, None]
        length = 0
        for i in xrange(4):
            x = unit * i
            self.font = self.fonts[i]
            self.dc.SetFont(self.font)
            wordPrintInfo[i], l = self.prepareText(texts[i], unit - (self.border[0]<<1))
            if l > length:
                length = l
        print 'prepare height:', length
        return wordPrintInfo, length
    
    def prepareText(self, lines, width):
        text = []
        for line in lines:
            text.extend(self.prepareSingleLine(line, width))
        height = 0
        for line in text:
            if line.strip():
                # height += self.dc.GetTextExtent(line)[1]
                height += int(self.font.GetPointSize() * self.fontHeightFactor)
        # height = sum(map(lambda t: self.dc.GetTextExtent(t)[1], text))
        return text, height
    
    def prepareSingleLine(self, line, width):
        text = [line]
        textExtents = self.dc.GetPartialTextExtents(line)
        for i, textExtent in zip(xrange(len(textExtents)), textExtents):
            if textExtent >= width:
                sep = i
                appender = ''
                try:
                    sp = line.rindex(' ', 0, i)
                except:
                    if line[i] == ' ':
                        sp = i
                    else:
                        sp = 0
                if sp > ((sep>>1)+(sep>>2)):
                    sep = sp                    
                elif 0x20 < ord(line[i]) < 0x7f:
                    sep = i-1
                    appender = '-'
                else:
                    sep = i
                text = [(line[:sep] + appender + os.linesep)]
                text.extend(self.prepareSingleLine(line[sep:], width))
                break
        return text
        
    def HasPage(self, page):
        print 'has page', page
        return bool(len(self.pages))
    
    def GetPageInfo(self):
        print 'get page info'
        return (1,len(self.pages),1,len(self.pages))
    
    def OnPrintPage(self, page):
        print 'on print page'
        self.pageSize = self.GetPageSizePixels()
        print self.pageSize
        self.seps = map(lambda x: x[0]/x[1]+1, zip(self.pageSize, self.sepLimits))
        print 'seps:', self.seps
        self.blockSize = map(lambda x: x[0] / x[1], zip(self.pageSize, self.seps))
        print self.blockSize
        
        self.dc = self.GetDC()
        dcSize = self.dc.GetSizeTuple()
        self.scale = min(float(dcSize[0]) / self.pageSize[0], float(dcSize[1]) / self.pageSize[1])
        self.dc.SetUserScale(self.scale, self.scale)
#        self.dc.SetFont(self.font)
        
        self.dc.BeginDrawing()
        self.draw(self.pages[page-1])
        self.dc.EndDrawing()
        
    def draw(self, page):
        for i in xrange(1, self.seps[0]):
            x = self.blockSize[0] * i
            self.dc.DrawLine(x, 0, x, self.pageSize[1])
        for i in xrange(1, self.seps[1]):
            y = self.blockSize[1] * i
            self.dc.DrawLine(0, y, self.pageSize[0], y)
        for n in xrange(self.seps[1]):
            for m in xrange(self.seps[0]):
                index = m * self.seps[1] + n
                if index < len(page):
                    x = self.blockSize[0] * m
                    y = self.blockSize[1] * n
                    print 'device origin:', (x,y)
                    self.dc.SetDeviceOrigin(x * self.scale, y * self.scale)
                    self.drawBlock(self.blockSize, page[index])
        
        
    def drawBlock(self, size, block):
        blockRect = [self.margin[0],                    #left
                     self.margin[1],                    #top
                     size[0] - (self.margin[0]<<1),     #width
                     size[1] - (self.margin[1]<<1)]     #height
        unit = ((size[0] - (self.margin[0]<<1)) >> 2)
        self.dc.SetPen(self.blockPen)
        self.dc.DrawRectangle(*blockRect)
        for i in xrange(1, 4):
            x = blockRect[0] + i * unit
            self.dc.DrawLine(x, blockRect[1], x, blockRect[1] + blockRect[3])
        for word, y in block:
            if y > blockRect[1]:
                self.dc.SetPen(self.wordPen)
                self.dc.DrawLine(blockRect[0], y, blockRect[0] + blockRect[2], y)
            self.drawWord(size, word, y)
        
    def drawWord(self, size, word, startY):
        unit = ((size[0] - (self.margin[0]<<1)) >> 2)
        height = 0
        print 'print start y:', startY + self.border[1]
        for i in xrange(len(word)):
            self.font = self.fonts[i]
            self.dc.SetFont(self.font)
            y = startY + self.border[1]
            x = self.margin[0] + self.border[0] + unit * i
            h = 0
            for line in word[i]:
                if line.strip():
                    self.dc.DrawText(line.strip(), x, y)
#                    y += self.dc.GetTextExtent(line)[1]
#                    h += self.dc.GetTextExtent(line)[1]
                    y += int(self.font.GetPointSize() * self.fontHeightFactor)
                    h += int(self.font.GetPointSize() * self.fontHeightFactor)
                    if height < y:
                        height = y
            print 'print height:', h
        print 'print y:', height
#        self.dc.SetPen(self.wordPen)
#        self.dc.DrawLine(self.margin[0], height, size[0] - (self.margin[1]<<1), height)
        
        
        
            

def getPrinter():
    return VocabularyPrinter.getInstance()
